Load Libraries

library(tidyverse)
library(tidycensus)
library(sf)
library(plotly)
library(crosstalk)
library(leaflet)
library(gganimate)

Get Census Data

nc_2016 <- 
  get_acs(geography = "county",
          variables = "B01003_001",
          state = "NC",
          geometry = TRUE) %>% 
  mutate(year = "2016")

nc_2015 <- 
  get_acs(geography = "county",
          variables = "B01003_001",
          state = "NC",
          year = 2015,
          geometry = TRUE) %>% 
  mutate(year = "2015")

nc_2014 <- 
  get_acs(geography = "county",
          variables = "B01003_001",
          state = "NC",
          year = 2014,
          geometry = TRUE) %>% 
  mutate(year = "2014")

nc_2013 <- 
  get_acs(geography = "county",
          variables = "B01003_001",
          state = "NC",
          year = 2013,
          geometry = TRUE) %>% 
  mutate(year = "2013")

Bind Rows

big_table <- rbind(nc_2016, nc_2015, nc_2014, nc_2013) 
glimpse(big_table)
Observations: 400
Variables: 7
$ GEOID    <chr> "37001", "37003", "37005", "37007", "37009", "3701...
$ NAME     <chr> "Alamance County, North Carolina", "Alexander Coun...
$ variable <chr> "B01003_001", "B01003_001", "B01003_001", "B01003_...
$ estimate <dbl> 157844, 37159, 10935, 25531, 26833, 17535, 47316, ...
$ moe      <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
$ year     <chr> "2016", "2016", "2016", "2016", "2016", "2016", "2...
$ geometry <MULTIPOLYGON [°]> MULTIPOLYGON (((-79.54192 3..., MULT...
big_table2 <- big_table
st_geometry(big_table2) <- NULL

Test

first: ggplot facet_wrap

ggplot(big_table) +
  geom_sf(aes(fill = estimate, color = estimate)) +
  facet_wrap(~ year)

Second: side by side bargraph

big_table2 %>% 
  mutate(county = str_extract(NAME, "\\w+")) %>% 
  filter(county == "Mecklenburg" | 
           county == "Durham" | 
           county == "Guilford") %>% 
  select(county, estimate, year) %>% 
  gather("foo", "year", -county, -estimate) %>% 
  ggplot() +
  geom_col(aes(x = county, y = estimate, fill = year), 
           position = "dodge") +
  scale_fill_viridis_d() +
  coord_flip() 

Third: Simple ggplotly for one year (2016) but no slider.

ggnc <- ggplot(nc_2016) +
  geom_sf(aes(fill = estimate, color = estimate)) 
ggplotly(ggnc)

fourth: crosstalk shared data & slider

shared_bigtable <- SharedData$new(big_table %>% 
  mutate(year = as.numeric(year)))
filter_slider("mapyear", "YEAR", shared_bigtable, 
              column=~year, timeFormat = "%Y")
GreenPalette <- colorNumeric(palette = "Greens",
                               domain = big_table$estimate)
big_table %>% 
  st_transform(crs = "+init=epsg:4326") %>%
  leaflet(width = "100%") %>%
  addProviderTiles(provider = "CartoDB.Positron") %>% 
  addPolygons(fillOpacity = 0.7,
              smoothFactor = 0,
              stroke = FALSE, 
              color = ~GreenPalette(estimate))
nc_share_leaf <- (shared_bigtable %>% 
  #st_transform(crs = "+init=epsg:4326") %>%
  leaflet(width = "100%") %>%
  addProviderTiles(provider = "CartoDB.Positron") %>% 
  addPolygons(fillOpacity = 0.7,
              smoothFactor = 0,
              stroke = FALSE, 
              color = ~GreenPalette(estimate)))
sf layer has inconsistent datum (+proj=longlat +datum=NAD83 +no_defs).
Need '+proj=longlat +datum=WGS84'

Fifth: annimate with gganimate

big3p
p

Frame 1 (1%)
Frame 2 (2%)
Frame 3 (3%)
Frame 4 (4%)
Frame 5 (5%)
Frame 6 (6%)
Frame 7 (7%)
Frame 8 (8%)
Frame 9 (9%)
Frame 10 (10%)
Frame 11 (11%)
Frame 12 (12%)
Frame 13 (13%)
Frame 14 (14%)
Frame 15 (15%)
Frame 16 (16%)
Frame 17 (17%)
Frame 18 (18%)
Frame 19 (19%)
Frame 20 (20%)
Frame 21 (21%)
Frame 22 (22%)
Frame 23 (23%)
Frame 24 (24%)
Frame 25 (25%)
Frame 26 (26%)
Frame 27 (27%)
Frame 28 (28%)
Frame 29 (29%)
Frame 30 (30%)
Frame 31 (31%)
Frame 32 (32%)
Frame 33 (33%)
Frame 34 (34%)
Frame 35 (35%)
Frame 36 (36%)
Frame 37 (37%)
Frame 38 (38%)
Frame 39 (39%)
Frame 40 (40%)
Frame 41 (41%)
Frame 42 (42%)
Frame 43 (43%)
Frame 44 (44%)
Frame 45 (45%)
Frame 46 (46%)
Frame 47 (47%)
Frame 48 (48%)
Frame 49 (49%)
Frame 50 (50%)
Frame 51 (51%)
Frame 52 (52%)
Frame 53 (53%)
Frame 54 (54%)
Frame 55 (55%)
Frame 56 (56%)
Frame 57 (57%)
Frame 58 (58%)
Frame 59 (59%)
Frame 60 (60%)
Frame 61 (61%)
Frame 62 (62%)
Frame 63 (63%)
Frame 64 (64%)
Frame 65 (65%)
Frame 66 (66%)
Frame 67 (67%)
Frame 68 (68%)
Frame 69 (69%)
Frame 70 (70%)
Frame 71 (71%)
Frame 72 (72%)
Frame 73 (73%)
Frame 74 (74%)
Frame 75 (75%)
Frame 76 (76%)
Frame 77 (77%)
Frame 78 (78%)
Frame 79 (79%)
Frame 80 (80%)
Frame 81 (81%)
Frame 82 (82%)
Frame 83 (83%)
Frame 84 (84%)
Frame 85 (85%)
Frame 86 (86%)
Frame 87 (87%)
Frame 88 (88%)
Frame 89 (89%)
Frame 90 (90%)
Frame 91 (91%)
Frame 92 (92%)
Frame 93 (93%)
Frame 94 (94%)
Frame 95 (95%)
Frame 96 (96%)
Frame 97 (97%)
Frame 98 (98%)
Frame 99 (99%)
Frame 100 (100%)
Finalizing encoding... done!
plot_13 <- ggplot(nc_2013) +
  geom_sf(aes(fill = estimate, color = estimate)) 
plot_13

Sixth: foo


ggnc <- ggplot() +
  geom_sf(data = nc_2016, 
          aes(fill = estimate, color = estimate)) +
  geom_sf(data = nc_2013, 
          aes(fill = estimate, color = estimate)) 

ggnc




ggpp <- ggplotly(ggnc) %>% 
  add_trace(
    z = ~estimate,
    frame = ~year)

ggpp

animation_slider(ggpp)
ggplotly(ggnc, tooltip = "NAME") %>% 
  layout(
    hovermode = "x",
    margin = list(
      t = 20,
      b = 20,
      l = 20,
      r = 20),
  )

County Level Plotly

https://moderndata.plot.ly/county-level-choropleth-in-plotly-and-r/

county_df <- map_data("county") 
nc_county <- county_df %>% filter(region == "north carolina")

  
state_df <- map_data("state") 
nc_df <- state_df %>% filter(region == "north carolina")

Make Plotly Map

boarder_white <- list(color = toRGB("white"), width = 2)

usa_projection <- list(scope = "usa", 
                       projection = list(type = "albers usa"),
                       showlakes = TRUE,
                       lakecolor = toRGB("white"))

plotlymap <- plot_geo(big_table, locationmode = 'county names') %>% 
  add_trace(
    z = ~estimate, text = ~estimate, location = ~NAME,
    color = ~estimate, colors = "Purples",
    frame = ~NAME
  ) %>% 
  layout = usa_projection

plotlymap
ggplotly()
LS0tDQp0aXRsZTogIlBsb3RseSBDaG9yb3BsZXRoIg0KYXV0aG9yOiAiSm9obiBMaXR0bGUiDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyBMb2FkIExpYnJhcmllcw0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeSh0aWR5Y2Vuc3VzKQ0KbGlicmFyeShzZikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShjcm9zc3RhbGspDQpsaWJyYXJ5KGxlYWZsZXQpDQpsaWJyYXJ5KGdnYW5pbWF0ZSkNCmBgYA0KDQoNCg0KIyMgR2V0IENlbnN1cyBEYXRhDQoNCmBgYHtyfQ0KbmNfMjAxNiA8LSANCiAgZ2V0X2FjcyhnZW9ncmFwaHkgPSAiY291bnR5IiwNCiAgICAgICAgICB2YXJpYWJsZXMgPSAiQjAxMDAzXzAwMSIsDQogICAgICAgICAgc3RhdGUgPSAiTkMiLA0KICAgICAgICAgIGdlb21ldHJ5ID0gVFJVRSkgJT4lIA0KICBtdXRhdGUoeWVhciA9ICIyMDE2IikNCg0KbmNfMjAxNSA8LSANCiAgZ2V0X2FjcyhnZW9ncmFwaHkgPSAiY291bnR5IiwNCiAgICAgICAgICB2YXJpYWJsZXMgPSAiQjAxMDAzXzAwMSIsDQogICAgICAgICAgc3RhdGUgPSAiTkMiLA0KICAgICAgICAgIHllYXIgPSAyMDE1LA0KICAgICAgICAgIGdlb21ldHJ5ID0gVFJVRSkgJT4lIA0KICBtdXRhdGUoeWVhciA9ICIyMDE1IikNCg0KbmNfMjAxNCA8LSANCiAgZ2V0X2FjcyhnZW9ncmFwaHkgPSAiY291bnR5IiwNCiAgICAgICAgICB2YXJpYWJsZXMgPSAiQjAxMDAzXzAwMSIsDQogICAgICAgICAgc3RhdGUgPSAiTkMiLA0KICAgICAgICAgIHllYXIgPSAyMDE0LA0KICAgICAgICAgIGdlb21ldHJ5ID0gVFJVRSkgJT4lIA0KICBtdXRhdGUoeWVhciA9ICIyMDE0IikNCg0KbmNfMjAxMyA8LSANCiAgZ2V0X2FjcyhnZW9ncmFwaHkgPSAiY291bnR5IiwNCiAgICAgICAgICB2YXJpYWJsZXMgPSAiQjAxMDAzXzAwMSIsDQogICAgICAgICAgc3RhdGUgPSAiTkMiLA0KICAgICAgICAgIHllYXIgPSAyMDEzLA0KICAgICAgICAgIGdlb21ldHJ5ID0gVFJVRSkgJT4lIA0KICBtdXRhdGUoeWVhciA9ICIyMDEzIikNCmBgYA0KDQojIyBCaW5kIFJvd3MNCg0KYGBge3J9DQpiaWdfdGFibGUgPC0gcmJpbmQobmNfMjAxNiwgbmNfMjAxNSwgbmNfMjAxNCwgbmNfMjAxMykgDQpnbGltcHNlKGJpZ190YWJsZSkNCg0KYmlnX3RhYmxlMiA8LSBiaWdfdGFibGUNCnN0X2dlb21ldHJ5KGJpZ190YWJsZTIpIDwtIE5VTEwNCmBgYA0KDQoNCiMjIFRlc3QNCg0KZmlyc3Q6ICBnZ3Bsb3QgZmFjZXRfd3JhcA0KDQpgYGB7cn0NCmdncGxvdChiaWdfdGFibGUpICsNCiAgZ2VvbV9zZihhZXMoZmlsbCA9IGVzdGltYXRlLCBjb2xvciA9IGVzdGltYXRlKSkgKw0KICBmYWNldF93cmFwKH4geWVhcikNCmBgYA0KDQoNClNlY29uZDogIHNpZGUgYnkgc2lkZSBiYXJncmFwaA0KDQpgYGB7cn0NCmJpZ190YWJsZTIgJT4lIA0KICBtdXRhdGUoY291bnR5ID0gc3RyX2V4dHJhY3QoTkFNRSwgIlxcdysiKSkgJT4lIA0KICBmaWx0ZXIoY291bnR5ID09ICJNZWNrbGVuYnVyZyIgfCANCiAgICAgICAgICAgY291bnR5ID09ICJEdXJoYW0iIHwgDQogICAgICAgICAgIGNvdW50eSA9PSAiR3VpbGZvcmQiKSAlPiUgDQogIHNlbGVjdChjb3VudHksIGVzdGltYXRlLCB5ZWFyKSAlPiUgDQogIGdhdGhlcigiZm9vIiwgInllYXIiLCAtY291bnR5LCAtZXN0aW1hdGUpICU+JSANCiAgZ2dwbG90KCkgKw0KICBnZW9tX2NvbChhZXMoeCA9IGNvdW50eSwgeSA9IGVzdGltYXRlLCBmaWxsID0geWVhciksIA0KICAgICAgICAgICBwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoKSArDQogIGNvb3JkX2ZsaXAoKSANCmBgYA0KDQpUaGlyZDogIFNpbXBsZSBnZ3Bsb3RseSBmb3Igb25lIHllYXIgKDIwMTYpIGJ1dCBubyBzbGlkZXIuDQoNCmBgYHtyfQ0KZ2duYyA8LSBnZ3Bsb3QobmNfMjAxNikgKw0KICBnZW9tX3NmKGFlcyhmaWxsID0gZXN0aW1hdGUsIGNvbG9yID0gZXN0aW1hdGUpKSANCg0KZ2dwbG90bHkoZ2duYykNCg0KDQpgYGANCg0KZm91cnRoOiAgY3Jvc3N0YWxrIHNoYXJlZCBkYXRhICYgc2xpZGVyDQoNCmBgYHtyfQ0Kc2hhcmVkX2JpZ3RhYmxlIDwtIFNoYXJlZERhdGEkbmV3KGJpZ190YWJsZSAlPiUgDQogIG11dGF0ZSh5ZWFyID0gYXMubnVtZXJpYyh5ZWFyKSkpDQpgYGANCg0KYGBge3J9DQpuY19zaGFyZV9zbGlkZSA8LSBmaWx0ZXJfc2xpZGVyKCJtYXB5ZWFyIiwgIllFQVIiLCBzaGFyZWRfYmlndGFibGUsIA0KICAgICAgICAgICAgICBjb2x1bW49fnllYXIsIHRpbWVGb3JtYXQgPSAiJVkiKQ0KYGBgDQoNCg0KYGBge3J9DQpHcmVlblBhbGV0dGUgPC0gY29sb3JOdW1lcmljKHBhbGV0dGUgPSAiR3JlZW5zIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb21haW4gPSBiaWdfdGFibGUkZXN0aW1hdGUpDQoNCmJpZ190YWJsZSAlPiUgDQogIHN0X3RyYW5zZm9ybShjcnMgPSAiK2luaXQ9ZXBzZzo0MzI2IikgJT4lDQogIGxlYWZsZXQod2lkdGggPSAiMTAwJSIpICU+JQ0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVyID0gIkNhcnRvREIuUG9zaXRyb24iKSAlPiUgDQogIGFkZFBvbHlnb25zKGZpbGxPcGFjaXR5ID0gMC43LA0KICAgICAgICAgICAgICBzbW9vdGhGYWN0b3IgPSAwLA0KICAgICAgICAgICAgICBzdHJva2UgPSBGQUxTRSwgDQogICAgICAgICAgICAgIGNvbG9yID0gfkdyZWVuUGFsZXR0ZShlc3RpbWF0ZSkpDQpgYGANCg0KDQpgYGB7cn0NCkdyZWVuUGFsZXR0ZSA8LSBjb2xvck51bWVyaWMocGFsZXR0ZSA9ICJHcmVlbnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvbWFpbiA9IGJpZ190YWJsZSRlc3RpbWF0ZSkNCg0KbmNfc2hhcmVfbGVhZiA8LSAoc2hhcmVkX2JpZ3RhYmxlICU+JSANCiAgI3N0X3RyYW5zZm9ybShjcnMgPSAiK2luaXQ9ZXBzZzo0MzI2IikgJT4lDQogIGxlYWZsZXQod2lkdGggPSAiMTAwJSIpICU+JQ0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVyID0gIkNhcnRvREIuUG9zaXRyb24iKSAlPiUgDQogIGFkZFBvbHlnb25zKGZpbGxPcGFjaXR5ID0gMC43LA0KICAgICAgICAgICAgICBzbW9vdGhGYWN0b3IgPSAwLA0KICAgICAgICAgICAgICBzdHJva2UgPSBGQUxTRSwgDQogICAgICAgICAgICAgIGNvbG9yID0gfkdyZWVuUGFsZXR0ZShlc3RpbWF0ZSkpKQ0KYGBgDQoNCg0KYGBge3J9DQpic2NvbHMobmNfc2hhcmVfc2xpZGUsIG5jX3NoYXJlX2xlYWYpDQpgYGANCg0KRmlmdGg6ICBhbm5pbWF0ZSB3aXRoIGdnYW5pbWF0ZQ0KDQpgYGB7cn0NCmJpZzMgPC0gYmlnX3RhYmxlICU+JSBtdXRhdGUoeWVhciA9IGFzLm51bWVyaWMoeWVhcikpDQoNCmJpZzNwIDwtIGdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gYmlnMyAlPiUgZmlsdGVyKHllYXIgPT0gMjAxNiksIA0KICAgICAgICAgIGFlcyhmaWxsID0gZXN0aW1hdGUsIGNvbG9yID0gZXN0aW1hdGUpKSArDQogIGdlb21fc2YoZGF0YSA9IGJpZzMgJT4lIGZpbHRlcih5ZWFyID09IDIwMTMpLA0KICAgICAgICAgIGFlcyhmaWxsID0gZXN0aW1hdGUsIGNvbG9yID0gZXN0aW1hdGUpKSArDQogIGNvb3JkX3NmKGNycyA9IDQyNjksIGRhdHVtID0gTkEpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArDQogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsNCiAgdmlld196b29tX21hbnVhbCgxLCAxLCB4bWluID0gdmlld3MkeG1pbiwgeG1heCA9IHZpZXdzJHhtYXgsIA0KICAgICAgICAgICAgICAgICAgIHltaW4gPSB2aWV3cyR5bWluLCB5bWF4ID0gdmlld3MkeW1heCwgd3JhcCA9IFRSVUUpDQogICMgZmFjZXRfd3JhcCh+IHllYXIpICsNCiAgIyAgbGFicyh0aXRsZSA9ICdZZWFyOiB7ZnJhbWVfdGltZX0nKSArDQogICN0cmFuc2l0aW9uX3RpbWUoeWVhcikgKw0KICAjZWFzZV9hZXMoJ2xpbmVhcicpDQoNCmJpZzNwDQoNCmFuaW1hdGUoYmlnM3AsIDEwMCwgMTApDQpgYGANCg0KDQpgYGB7cn0NCmVhcnRoIDwtIHNmOjpzdF9hc19zZihybmF0dXJhbGVhcnRoOjpjb3VudHJpZXMxMTApDQp2aWV3cyA8LSBkYXRhLmZyYW1lKHJiaW5kKA0KICBzdF9iYm94KGVhcnRoW2VhcnRoJG5hbWUgPT0gJ0Rlbm1hcmsnLF0pLA0KICBzdF9iYm94KGVhcnRoW2VhcnRoJG5hbWUgPT0gJ0F1c3RyYWxpYScsXSkNCikpDQpwIDwtIGdncGxvdCgpICsgDQogIGdlb21fc2YoZGF0YSA9IGVhcnRoLCBmaWxsID0gJ3doaXRlJykgKyANCiAgZ2VvbV9zZihkYXRhID0gZWFydGhbZWFydGgkbmFtZSAlaW4lIGMoJ0Rlbm1hcmsnLCAnQXVzdHJhbGlhJyksXSwgZmlsbCA9ICdmb3Jlc3RncmVlbicpICsgDQogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoJ2xpZ2h0Ymx1ZScpKSArIA0KICB2aWV3X3pvb21fbWFudWFsKDEsIDEsIHhtaW4gPSB2aWV3cyR4bWluLCB4bWF4ID0gdmlld3MkeG1heCwgeW1pbiA9IHZpZXdzJHltaW4sIHltYXggPSB2aWV3cyR5bWF4LCB3cmFwID0gVFJVRSkNCmFuaW1hdGUocCwgMTAwLCAxMCkNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCnBsb3RfMTMgPC0gZ2dwbG90KG5jXzIwMTMpICsNCiAgZ2VvbV9zZihhZXMoZmlsbCA9IGVzdGltYXRlLCBjb2xvciA9IGVzdGltYXRlKSkgDQoNCnBsb3RfMTQgPC0gZ2dwbG90KG5jXzIwMTQpICsNCiAgZ2VvbV9zZihhZXMoZmlsbCA9IGVzdGltYXRlLCBjb2xvciA9IGVzdGltYXRlKSkgDQoNCnBsb3RfMTUgPC0gZ2dwbG90KG5jXzIwMTUpICsNCiAgZ2VvbV9zZihhZXMoZmlsbCA9IGVzdGltYXRlLCBjb2xvciA9IGVzdGltYXRlKSkgDQoNCnBsb3RfMTYgPC0gZ2dwbG90KG5jXzIwMTYpICsNCiAgZ2VvbV9zZihhZXMoZmlsbCA9IGVzdGltYXRlLCBjb2xvciA9IGVzdGltYXRlKSkgDQoNCnBsb3RfMTMNCmBgYA0KDQoNCg0KU2l4dGg6ICBmb28NCg0KYGBge3J9DQoNCmdnbmMgPC0gZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSBuY18yMDE2LCANCiAgICAgICAgICBhZXMoZmlsbCA9IGVzdGltYXRlLCBjb2xvciA9IGVzdGltYXRlKSkgKw0KICBnZW9tX3NmKGRhdGEgPSBuY18yMDEzLCANCiAgICAgICAgICBhZXMoZmlsbCA9IGVzdGltYXRlLCBjb2xvciA9IGVzdGltYXRlKSkgDQoNCmdnbmMNCg0KDQoNCg0KZ2dwcCA8LSBnZ3Bsb3RseShnZ25jKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICB6ID0gfmVzdGltYXRlLA0KICAgIGZyYW1lID0gfnllYXIpDQoNCmdncHANCg0KYW5pbWF0aW9uX3NsaWRlcihnZ3BwKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmdncGxvdGx5KGdnbmMsIHRvb2x0aXAgPSAiTkFNRSIpICU+JSANCiAgbGF5b3V0KA0KICAgIGhvdmVybW9kZSA9ICJ4IiwNCiAgICBtYXJnaW4gPSBsaXN0KA0KICAgICAgdCA9IDIwLA0KICAgICAgYiA9IDIwLA0KICAgICAgbCA9IDIwLA0KICAgICAgciA9IDIwKSwNCiAgKQ0KYGBgDQoNCg0KDQoNCg0KDQoNCiMjIENvdW50eSBMZXZlbCBQbG90bHkgDQoNCmh0dHBzOi8vbW9kZXJuZGF0YS5wbG90Lmx5L2NvdW50eS1sZXZlbC1jaG9yb3BsZXRoLWluLXBsb3RseS1hbmQtci8NCg0KYGBge3J9DQpjb3VudHlfZGYgPC0gbWFwX2RhdGEoImNvdW50eSIpIA0KbmNfY291bnR5IDwtIGNvdW50eV9kZiAlPiUgZmlsdGVyKHJlZ2lvbiA9PSAibm9ydGggY2Fyb2xpbmEiKQ0KDQogIA0Kc3RhdGVfZGYgPC0gbWFwX2RhdGEoInN0YXRlIikgDQpuY19kZiA8LSBzdGF0ZV9kZiAlPiUgZmlsdGVyKHJlZ2lvbiA9PSAibm9ydGggY2Fyb2xpbmEiKQ0KDQoNCmBgYA0KDQoNCiMjIE1ha2UgUGxvdGx5IE1hcA0KDQpgYGB7cn0NCmJvYXJkZXJfd2hpdGUgPC0gbGlzdChjb2xvciA9IHRvUkdCKCJ3aGl0ZSIpLCB3aWR0aCA9IDIpDQoNCnVzYV9wcm9qZWN0aW9uIDwtIGxpc3Qoc2NvcGUgPSAidXNhIiwgDQogICAgICAgICAgICAgICAgICAgICAgIHByb2plY3Rpb24gPSBsaXN0KHR5cGUgPSAiYWxiZXJzIHVzYSIpLA0KICAgICAgICAgICAgICAgICAgICAgICBzaG93bGFrZXMgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICBsYWtlY29sb3IgPSB0b1JHQigid2hpdGUiKSkNCg0KcGxvdGx5bWFwIDwtIHBsb3RfZ2VvKGJpZ190YWJsZSwgbG9jYXRpb25tb2RlID0gJ2NvdW50eSBuYW1lcycpICU+JSANCiAgYWRkX3RyYWNlKA0KICAgIHogPSB+ZXN0aW1hdGUsIHRleHQgPSB+ZXN0aW1hdGUsIGxvY2F0aW9uID0gfk5BTUUsDQogICAgY29sb3IgPSB+ZXN0aW1hdGUsIGNvbG9ycyA9ICJQdXJwbGVzIiwNCiAgICBmcmFtZSA9IH5OQU1FDQogICkgJT4lIA0KICBsYXlvdXQgPSB1c2FfcHJvamVjdGlvbg0KDQpwbG90bHltYXANCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90bHkoKQ0KYGBgDQoNCg0KDQo=